package com.goldmantis.alitrip.common.shiro;

import com.alibaba.fastjson.JSON;
import com.goldmantis.alitrip.common.ReturnMessage;
import com.goldmantis.alitrip.common.utils.ToolUtil;
import com.goldmantis.alitrip.common.UserThreadLocal;
import com.goldmantis.alitrip.common.redis.RedisService;
import com.goldmantis.alitrip.pojo.UvSysUser;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class JWTFilter extends BasicHttpAuthenticationFilter {

    private Logger LOGGER = LoggerFactory.getLogger(this.getClass());

    public static final String TOKEN_PREFIX = "Bearer ";
    public static final String HEADER_SCHEMA = "Authorization";
    public static final String ACCESS_TOKEN = "accessToken";

    @Autowired
    private RedisService redisService;

    public static String getAccessToken(HttpServletRequest httpRequest) {
        String token = httpRequest.getHeader(HEADER_SCHEMA);
        if (StringUtils.isEmpty(token)) {
            token = httpRequest.getParameter(ACCESS_TOKEN.toLowerCase());
            if (StringUtils.isEmpty(token)) {
                Cookie[] cookies = httpRequest.getCookies();
                if (cookies != null) {
                    for (Cookie cookie : cookies) {
                        if (StringUtils.equalsIgnoreCase(ACCESS_TOKEN, cookie.getName())) {
                            token = cookie.getValue();
                            break;
                        }
                    }
                }
            }
        }
        return token;
    }

    @Override
    protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authorization = getAccessToken(httpServletRequest);

        //检测到Authorization 需要走jwt filter(登陆除外)
        if (StringUtils.isNotBlank(authorization)&& !StringUtils.equalsIgnoreCase(httpServletRequest.getRequestURI(),"/rest/login")) {
            if (!StringUtils.startsWithIgnoreCase(authorization, TOKEN_PREFIX.toLowerCase())) {
                throw new UnauthenticatedException("令牌必须以bearer 开始，包含空格！");
            }
            if (StringUtils.isNotBlank(authorization) && StringUtils.startsWithIgnoreCase(authorization, TOKEN_PREFIX.toLowerCase())) {
                authorization = StringUtils.split(authorization, " ")[1];
                String userString = redisService.get(authorization);
                if (StringUtils.isEmpty(userString)) {
                    throw new UnauthenticatedException("令牌无效，可能是长时间未登陆，系统退出！请重新申请令牌！");
                }
                return true;
            }
        }
        //默认逻辑
        return false;

    }

    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String authorization = getAccessToken(httpServletRequest);
        ;
        if (StringUtils.isNotBlank(authorization) && StringUtils.startsWithIgnoreCase(authorization, TOKEN_PREFIX.toLowerCase())) {
            authorization = StringUtils.split(authorization, " ")[1];
        }

        UvSysUser user = JSON.parseObject(redisService.get(authorization), UvSysUser.class);
        UserThreadLocal.setUser(user);

        JWTToken token = new JWTToken(user.getAccount(),user.getOriginalPassword(),user);
        // 提交给realm进行登入，如果错误他会抛出异常并被捕获
        getSubject(request, response).login(token);
        // 如果没有抛出异常则代表登入成功，返回true


        //刷新redis的有效期
        redisService.expire(authorization, ToolUtil.REDIS_TOKEN_EXPIRE);
        return true;

    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (isLoginAttempt(request, response)) {
            try {
                executeLogin(request, response);
            } catch (Exception e) {
                throw new ShiroException(e.getMessage());
            }
        }
        return true;
    }

    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        try {
            HttpServletRequest httpServletRequest = (HttpServletRequest) request;
            HttpServletResponse httpServletResponse = (HttpServletResponse) response;
            httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
            httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
            httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));
            // 跨域时会首先发送一个option请求，这里我们给option请求直接返回正常状态
            if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
                httpServletResponse.setStatus(HttpStatus.OK.value());
                return false;
            }
            return super.preHandle(request, response);
        } catch (Exception e) {
            HttpServletResponse servletResponse = (HttpServletResponse) response;

            if (e instanceof UnauthenticatedException) {
                servletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            } else if (e instanceof UnauthorizedException) {
                servletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
            } else {
                servletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            }
            servletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);

            servletResponse.getWriter().print(JSON.toJSONString(ReturnMessage.failure(e.getMessage())));
            servletResponse.getWriter().flush();

            return false;
        }
    }


    @Override
    public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
        UserThreadLocal.setUser(null);
        UserThreadLocal.remove();
    }


}
